package com.example.util;

import com.alibaba.fastjson.JSONObject;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.*;
import java.util.*;

/**
 * @author YanMing
 */
public class RequestHostUtil {
    public static final String LOOPBACK_ADDRESS = "127.0.0.1";
    public static final String LOCALHOST_ADDRESS = "0:0:0:0:0:0:0:1";
    public static final String LOCALHOST = "localhost";

    public static void main(String[] args) throws SocketException, UnknownHostException {
        getLocalMacAddress();
    }

    /**
     * 描述：获取IP地址
     *
     * @param request
     * @return
     */
    public static String getIpAddress(HttpServletRequest request) {

        String ip = request.getHeader("x-forwarded-for");

        if (ip == null || ip.length() == 0 || "nuknown".equalsIgnoreCase(ip)) {

            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "nuknown".equalsIgnoreCase(ip)) {

            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "nuknown".equalsIgnoreCase(ip)) {

            ip = request.getRemoteAddr();
        }
        if (LOCALHOST_ADDRESS.equals(ip)) {
            ip = "localhost";
        }
        return ip;
    }


    /**
     * 根据IP地址获取mac地址
     *
     * @param ipAddress 127.0.0.1
     * @return
     */
    public static String getMac(String ipAddress) {
        String macAddress = "";
        try {
            // 如果为127.0.0.1,则获取本地MAC地址。
            if (LOOPBACK_ADDRESS.equals(ipAddress) || LOCALHOST_ADDRESS.equals(ipAddress) || LOCALHOST.equals(ipAddress)) {
                Set<String> macSet =getLocalMacAddress();
                if(macSet!=null && macSet.size()>0){
                    Iterator<String> it = macSet.iterator();
                    while (it.hasNext()) {
                        //判断是否有下一个,取出元素。
                        macAddress = it.next();
                    }
                }else{
                    macAddress = getMac_local();
                }
            } else {
                // 获取非本地IP的MAC地址
                //macAddress = getMac_nbtstat(ipAddress);
                macAddress = getMac_arp(ipAddress);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return macAddress;
    }

    /*
     * 通过路由表找到连接默认网关的ip接口，通过这些ip接口（InetAddress），对应上他们所在的网络接口（NetworkInterface），然后获取网络接口的mac地址
     * @return Set<String>：（a0-xx-xx-xx-xx-cb，0c-xx-xx-xx-xx-39)
     */
    public static Set<String> getLocalMacAddress() throws UnknownHostException, SocketException {
        Set<String> macSet = new HashSet<String>();

        for(Object ipAndGatewayObj : getIpAndGateway()){
            //String gateway = ((JSONObject)ipAndGatewayObj).get("gateway").toString();
            String ip = ((JSONObject)ipAndGatewayObj).get("ip").toString();
            InetAddress ia = InetAddress.getByName(ip);
            NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
            byte[] mac = ni.getHardwareAddress();
            //byte->int->16进制->string
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < mac.length; i++) {
                if (i != 0) {
                    sb.append("-");
                }
                String tmp = Integer.toHexString(mac[i] & 0xFF);// 将byte转为正整数。然后转为16进制数
                sb.append(tmp.length() == 1 ? 0 + tmp : tmp);
            }
            System.out.println("网络接口名称ni："+ni.getName()+"---"+"mac地址："+sb.toString().toUpperCase()+"---"+"ip地址："+ip);
            macSet.add(sb.toString().toUpperCase());
        }
        return macSet;

    }

    /*
     * 通过路由表的目的网络是'0.0.0.0'获得网关
     * @return List<JSONObject> (网关,ip)
     */
    public static List getIpAndGateway(){
        String os = System.getProperty("os.name");
        List<JSONObject> netInfoList = new ArrayList<JSONObject>();
        if (os != null && os.startsWith("Windows")) {
            try{
                String command = "route print";
                Process p = Runtime.getRuntime().exec(command);
                BufferedReader br = new BufferedReader(new InputStreamReader(
                        p.getInputStream()));
                String line;

                String[] tmp = null;
                JSONObject netInfo = null;
                while ((line = br.readLine()) != null) {
                    tmp = line.trim().split("\\s+");
                    if (tmp.length > 0 && tmp[0].equals("0.0.0.0")) {
                        netInfo = new JSONObject();
                        netInfo.put("gateway", tmp[2]);
                        netInfo.put("ip", tmp[3]);
                        netInfoList.add(netInfo);
                    }
                }
                br.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return netInfoList;
    }

    /*
     * 当不确定网卡名字时，要获得连接上网的网卡mac地址。
     * 通过traceroute的第一跳获得默认网关，然后获取本机的所有网络接口（NetworkInterface）以及每个网络接口的接口地址（InetAddress），
     * 通过对比接口地址IP与网关是不是在同一个网段下(根据网络前缀即子网掩码来验证)，来判定这个接口地址ip所在的网络接口（NetworkInterface）是连接上网的那个接口，然后他的mac地址就是我们要找的
     * @return Set<String>：（a0-xx-xx-xx-xx-cb，0c-xx-xx-xx-xx-xx)
     */
    public static Set<String> getLocalMac() throws Exception{
        Set<String> macSet = new HashSet<String>();
        InetAddress ip = null;
        String gateway = getGateway();

        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
        while(interfaces.hasMoreElements()){
            NetworkInterface ni = interfaces.nextElement();
            if(!ni.isVirtual() && !ni.isLoopback() && ni.isUp()){
                List<InterfaceAddress> infs = ni.getInterfaceAddresses();
                for(InterfaceAddress inf : infs){
                    ip = inf.getAddress();
                    if(ip!=null && ip instanceof Inet4Address && !ip.isLoopbackAddress()){
                        if(ip.isSiteLocalAddress()){
                            String ipStr = ip.getHostAddress();
                            //获得网络前缀
                            short maskLen = inf.getNetworkPrefixLength();
                            if(getNetworkNumber(ipStr, maskLen).equals(getNetworkNumber(gateway, maskLen))){//对比是否在同一个网段
                                //byte->int->16进制->string
                                byte[] mac = ni.getHardwareAddress();
                                StringBuffer sb = new StringBuffer();
                                for(int i=0; i<mac.length; i++){
                                    if(i!=0) {
                                        sb.append("-");
                                    }
                                    String tmp = Integer.toHexString(mac[i]&0xFF);//将byte转为正整数。然后转为16进制数
                                    sb.append(tmp.length()==1?0+tmp:tmp);
                                }
                                System.out.println("网络接口名称ni："+ni.getName()+"---"+"mac地址："+sb.toString().toUpperCase()+"---"+"ip地址："+ipStr);
                                macSet.add(sb.toString().toUpperCase());
                            }
                        }
                    }
                }
            }
        }
        return macSet;
    }


    /*
     * 通过traceroute的第一跳获得网关，缺点：没有连接网络时获取不到
     * @return String 网关
     */
    public static String getGateway(){
        String os = System.getProperty("os.name");

        if (os != null && os.startsWith("Windows")) {
            try{
                String command = "tracert -d www.baidu.com";
                Process p = Runtime.getRuntime().exec(command);
                BufferedReader br = new BufferedReader(new InputStreamReader(
                        p.getInputStream()));
                String line;

                String[] tmp = null;
                while ((line = br.readLine()) != null){
                    if(line.trim().startsWith("1")){
                        tmp = line.trim().split("\\s+");
                        if(tmp.length>0 && tmp[0].equals("1")){
                            System.out.println("网关："+tmp[tmp.length-1]);
                            return tmp[tmp.length-1];
                        }
                    }
                }
                br.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return null;
    }
    /*
     * 获得某ip地址所在网络的网络号
     * @param ip
     * @param masklen 掩码长度
     * @return 网络号
     */
    public static String getNetworkNumber(String ip, short masklen){
        String[] ipStr = ip.split("\\.");
        StringBuffer sb = new StringBuffer();
        sb.append(ipStr[0]);
        for(int i=1; i<ipStr.length; i++){
            if(i<masklen/8) {
                sb.append("." + ipStr[i]);
            }else {
                sb.append(".0");
            }
        }
        return sb.toString();
    }
    /*
     * 通过网卡名称获取该网卡的mac地址
     * @param networkCardName 网卡名，如eth7
     * @return mac地址字符串：a0-xx-xx-xx-xx-cb
     */
    public static String getMacAddressByName(String networkCardName) throws SocketException {
        NetworkInterface ni = NetworkInterface.getByName(networkCardName);
        byte[] mac = ni.getHardwareAddress();
        //byte->int->16进制->string
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < mac.length; i++) {
            if (i != 0) {
                sb.append("-");
            }
            String tmp = Integer.toHexString(mac[i] & 0xFF);// 将byte转为正整数。然后转为16进制数
            sb.append(tmp.length() == 1 ? 0 + tmp : tmp);
        }

        return sb.toString().toUpperCase();
    }
    /**
     * 多网卡指定名字类型返回指定InetAddress
     * 输入类型错误则输出全部网卡接口信息
     *
     * @param Name 名字
     * @param type 类型:  4--Inet4Address  6--Inet6Address
     * @return
     */
    public static InetAddress getInetAddress(String Name, int type) {
        try {
            Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces();
            InetAddress addr;
            if (type == 4) {
                while (nifs.hasMoreElements()) {
                    NetworkInterface nif = nifs.nextElement();
                    Enumeration<InetAddress> addresses = nif.getInetAddresses();
                    while (addresses.hasMoreElements()) {
                        addr = addresses.nextElement();
                        if (nif.getName().equals(Name) && addr instanceof Inet4Address) {
                            return addr;
                        }
                    }
                }
            } else if (type == 6) {
                while (nifs.hasMoreElements()) {
                    NetworkInterface nif = nifs.nextElement();
                    Enumeration<InetAddress> addresses = nif.getInetAddresses();
                    while (addresses.hasMoreElements()) {
                        addr = addresses.nextElement();
                        if (nif.getName().equals(Name) && addr instanceof Inet6Address) {
                            return addr;
                        }
                    }
                }
            } else {
                System.out.println("类型指定错误,将输出全部网卡接口信息");
                System.out.println();
                while (nifs.hasMoreElements()) {
                    NetworkInterface nif = nifs.nextElement();
                    Enumeration<InetAddress> addresses = nif.getInetAddresses();
                    while (addresses.hasMoreElements()) {
                        addr = addresses.nextElement();
                        System.out.println("网卡接口名称：" + nif.getName());
                        System.out.println("网卡接口地址：" + addr.getHostAddress());
                        System.out.println();
                    }
                }
                return null;
            }

        } catch (Exception e) {
            System.out.println("获取Socket失败");
        }
        return null;
    }


    private static String getMac_local() throws Exception {
        InetAddress localHost = InetAddress.getLocalHost();
        //获取网卡，获取地址
        byte[] mac = NetworkInterface.getByInetAddress(localHost).getHardwareAddress();
        //System.out.println("mac数组长度："+mac.length);
        StringBuffer sb = new StringBuffer("");
        for(int i=0; i<mac.length; i++) {
            if(i!=0) {
                sb.append("-");
            }
            //字节转换为整数
            int temp = mac[i]&0xff;
            String str = Integer.toHexString(temp);
            //System.out.println("每8位:"+str);
            if(str.length()==1) {
                sb.append("0"+str);
            }else {
                sb.append(str);
            }
        }
        return sb.toString().toUpperCase();

    }

    public static String getMac_arp(String ip) {
        String mac = "";
        try {
            Process p = Runtime.getRuntime().exec("arp -n");
            InputStreamReader ir = new InputStreamReader(p.getInputStream());
            LineNumberReader input = new LineNumberReader(ir);
            p.waitFor();
            boolean flag = true;
            String ipStr = "(" + ip + ")";
            while (flag) {
                String str = input.readLine();
                if (str != null) {
                    if (str.indexOf(ipStr) > 1) {
                        int temp = str.indexOf("at");
                        mac = str.substring(
                                temp + 3, temp + 20);
                        break;
                    }
                } else {
                    flag = false;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mac;
    }

    /**
     * 调用window的命令，在后台Bean里实现 通过ip来获取mac地址。方法如下：
     * 运行速度【快】
     *
     * @return
     */
    public static String getMac_ipconfig() {
        String mac = null;
        try {
            Process pro = Runtime.getRuntime().exec("cmd.exe /c ipconfig/all");

            InputStream is = pro.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String message = br.readLine();

            int index = -1;
            while (message != null) {
                if ((index = message.indexOf("Physical Address")) > 0) {
                    mac = message.substring(index + 36).trim();
                    break;
                }
                message = br.readLine();
            }
            System.out.println(mac);
            br.close();
            pro.destroy();
        } catch (IOException e) {
            System.out.println("Can't get mac address!");
            return null;
        }
        return mac;
    }

    /**
     * 运行速度【慢】
     *
     * @param ip
     * @return
     */
    public static String getMac_nbtstat(String ip) {
        String macAddress = null;
        Process p = null;
        InputStreamReader ir = null;
        BufferedReader br = null;
        try {
            String str = null;
            p = Runtime.getRuntime().exec("nbtstat -A " + ip);
            System.out.println("===process==" + p);
            ir = new InputStreamReader(p.getInputStream());
            br = new BufferedReader(ir);
            while ((str = br.readLine()) != null) {
                if (str.indexOf("MAC") > 1) {
                    macAddress = str.substring(str.indexOf("MAC") + 9, str.length());
                    macAddress = macAddress.trim();
                    System.out.println("macAddress:" + macAddress);
                    break;
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (p != null) {
                p.destroy();
            }
            try {
                if (br != null) {
                    br.close();
                }
                if (ir != null) {
                    ir.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return macAddress;
    }

    /**
     * 描述：获取IP+[IP所属地址]
     *
     * @return
     */
    public static String getIpBelongAddress(HttpServletRequest request) {

        String ip = getIpAddress(request);
        String belongIp = getIPbelongAddress(ip);

        return ip + belongIp;
    }

    /**
     * 描述：获取IP所属地址
     *
     * @param ip
     * @return
     */
    public static String getIPbelongAddress(String ip) {

        String ipAddress = "[]";
        try {
//淘宝提供的服务地址
            String context = call("http://ip.taobao.com/service/getIpInfo.php?ip=" + ip);
            JSONObject fromObject = JSONObject.parseObject(context);
            String code = fromObject.getString("code");
            if (code.equals("0")) {
                JSONObject jsonObject = fromObject.getJSONObject("data");
                ipAddress = "[" + jsonObject.get("country") + "/" + jsonObject.get("city") + "]";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ipAddress;
    }

    /**
     * 描述：获取Ip所属地址
     *
     * @param urlStr
     * @return
     * @author huaping hu
     * @date 2016年6月1日下午5:38:55
     */
    public static String call(String urlStr) {

        try {

            URL url = new URL(urlStr);
            HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();

            httpCon.setConnectTimeout(3000);
            httpCon.setDoInput(true);
            httpCon.setRequestMethod("GET");

            int code = httpCon.getResponseCode();

            if (code == 200) {
                return streamConvertToSting(httpCon.getInputStream());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 描述：将InputStream转换成String
     *
     * @param is
     * @return
     * @author huaping hu
     * @date 2016年6月1日下午5:51:53
     */
    public static String streamConvertToSting(InputStream is) {

        String tempStr = "";
        try {

            if (is == null) {
                return null;
            }
            ByteArrayOutputStream arrayOut = new ByteArrayOutputStream();
            byte[] by = new byte[1024];
            int len = 0;
            while ((len = is.read(by)) != -1) {
                arrayOut.write(by, 0, len);
            }
            tempStr = new String(arrayOut.toByteArray());

        } catch (IOException e) {
// TODO Auto-generated catch block
            e.printStackTrace();
        }
        return tempStr;
    }


}
